﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ModelComponentModelMapper.cs" company="DissingNelleman">
//   
// All rights reserved. </copyright>
// <summary>
//   The model component model mapper.
// </summary>
// --------------------------------------------------------------------------------------------------------------------


using NHibernate.Mapping.ByCode;
using NHibernate.Type;

namespace LiSA_Server.Base.NHibernate
{
    using System;
    using System.Linq;
    using System.Reflection;

    /// <summary>
    /// The model component model mapper.
    /// </summary>
    internal class ModelComponentModelMapper : ModelMapper
    {
        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="ModelComponentModelMapper"/> class.
        /// </summary>
        public ModelComponentModelMapper()
            : base(new ModelComponentModelInspector())
        {
            this.BeforeMapAny += this.MapAny;
            this.BeforeMapBag += this.MapBag;
            this.BeforeMapClass += this.MapClass;
            this.BeforeMapComponent += this.MapComponent;
            this.BeforeMapElement += this.MapElement;
            this.BeforeMapIdBag += this.MapIdBag;
            this.BeforeMapJoinedSubclass += this.MapJoinedSubclass;
            this.BeforeMapList += this.MapList;
            this.BeforeMapManyToMany += this.MapManyToMany;
            this.BeforeMapManyToOne += this.MapManyToOne;
            this.BeforeMapMap += this.MapMap;
            this.BeforeMapMapKey += this.MapMapKey;
            this.BeforeMapMapKeyManyToMany += this.MapMapKeyManyToMany;
            this.BeforeMapOneToMany += this.MapOneToMany;
            this.BeforeMapOneToOne += this.MapOneToOne;
            this.BeforeMapProperty += this.MapProperty;
            this.BeforeMapSet += this.MapSet;
            this.BeforeMapSubclass += this.MapSubclass;
            this.BeforeMapUnionSubclass += this.MapUnionSubclass;
        }

        #endregion

        #region Methods

        /// <summary>
        /// The entity table name.
        /// </summary>
        /// <param name="entityType">
        /// The entity type.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        private string EntityTableName(Type entityType)
        {
            string[] fullname =
                entityType.FullName.Split(new[] { "." }, StringSplitOptions.RemoveEmptyEntries)
                          .Reverse()
                          .Take(2)
                          .ToArray();
            
            // TO AVOID RENAMING OF EXISTING DATABASE:
            return string.Format("Models_{0}", fullname[0]);
            //return string.Format("{0}_{1}", fullname[1], fullname[0]);
        }

        /// <summary>
        /// The map any.
        /// </summary>
        /// <param name="modelInspector">
        /// The model inspector.
        /// </param>
        /// <param name="member">
        /// The member.
        /// </param>
        /// <param name="propertyCustomizer">
        /// The property customizer.
        /// </param>
        private void MapAny(IModelInspector modelInspector, PropertyPath member, IAnyMapper propertyCustomizer)
        {
            propertyCustomizer.Columns(
                id => { id.Name(string.Format("{0}_ID", member.LocalMember.Name)); },
                classType => { classType.Name(string.Format("{0}_Type", member.LocalMember.Name)); });
        }

        /// <summary>
        /// The map bag.
        /// </summary>
        /// <param name="modelInspector">
        /// The model inspector.
        /// </param>
        /// <param name="member">
        /// The member.
        /// </param>
        /// <param name="propertyCustomizer">
        /// The property customizer.
        /// </param>
        private void MapBag(
            IModelInspector modelInspector, PropertyPath member, IBagPropertiesMapper propertyCustomizer)
        {
            propertyCustomizer.Table(
                string.Format(
                    "{0}_{1}", this.EntityTableName(member.LocalMember.DeclaringType), member.LocalMember.Name));
            propertyCustomizer.Key(
                k => { k.Column(c => { c.Name(string.Format("Owner_{0}", member.LocalMember.DeclaringType.Name)); }); });
        }

        /// <summary>
        /// The map class.
        /// </summary>
        /// <param name="modelInspector">
        /// The model inspector.
        /// </param>
        /// <param name="type">
        /// The type.
        /// </param>
        /// <param name="classCustomizer">
        /// The class customizer.
        /// </param>
        private void MapClass(IModelInspector modelInspector, Type type, IClassAttributesMapper classCustomizer)
        {
            classCustomizer.Table(this.EntityTableName(type));
            classCustomizer.Id(
                type.GetProperty("ID"),
                m =>
                {
                    m.Generator(new AssignedGeneratorDef());
                    m.Type(new GuidType());
                });
            classCustomizer.Version(type.GetProperty("Version"), v => { });
        }

        /// <summary>
        /// The map component.
        /// </summary>
        /// <param name="modelInspector">
        /// The model inspector.
        /// </param>
        /// <param name="member">
        /// The member.
        /// </param>
        /// <param name="propertyCustomizer">
        /// The property customizer.
        /// </param>
        private void MapComponent(
            IModelInspector modelInspector, PropertyPath member, IComponentAttributesMapper propertyCustomizer)
        {
        }

        /// <summary>
        /// The map element.
        /// </summary>
        /// <param name="modelInspector">
        /// The model inspector.
        /// </param>
        /// <param name="member">
        /// The member.
        /// </param>
        /// <param name="collectionRelationElementCustomizer">
        /// The collection relation element customizer.
        /// </param>
        private void MapElement(
            IModelInspector modelInspector, PropertyPath member, IElementMapper collectionRelationElementCustomizer)
        {
            Type containingType = (member.LocalMember as PropertyInfo).PropertyType;
            collectionRelationElementCustomizer.Column(string.Format("{0}_Element", member.LocalMember.Name));
        }

        /// <summary>
        /// The map id bag.
        /// </summary>
        /// <param name="modelInspector">
        /// The model inspector.
        /// </param>
        /// <param name="member">
        /// The member.
        /// </param>
        /// <param name="propertyCustomizer">
        /// The property customizer.
        /// </param>
        private void MapIdBag(
            IModelInspector modelInspector, PropertyPath member, IIdBagPropertiesMapper propertyCustomizer)
        {
            propertyCustomizer.Table(
                string.Format(
                    "{0}_{1}", this.EntityTableName(member.LocalMember.DeclaringType), member.LocalMember.Name));
            propertyCustomizer.Key(
                k => { k.Column(c => { c.Name(string.Format("Owner_{0}", member.LocalMember.DeclaringType.Name)); }); });
            propertyCustomizer.Id(id => { id.Column("Target_ID"); });
        }

        /// <summary>
        /// The map joined subclass.
        /// </summary>
        /// <param name="modelInspector">
        /// The model inspector.
        /// </param>
        /// <param name="type">
        /// The type.
        /// </param>
        /// <param name="joinedSubclassCustomizer">
        /// The joined subclass customizer.
        /// </param>
        private void MapJoinedSubclass(
            IModelInspector modelInspector, Type type, IJoinedSubclassAttributesMapper joinedSubclassCustomizer)
        {
        }

        /// <summary>
        /// The map list.
        /// </summary>
        /// <param name="modelInspector">
        /// The model inspector.
        /// </param>
        /// <param name="member">
        /// The member.
        /// </param>
        /// <param name="propertyCustomizer">
        /// The property customizer.
        /// </param>
        private void MapList(
            IModelInspector modelInspector, PropertyPath member, IListPropertiesMapper propertyCustomizer)
        {
            propertyCustomizer.Table(
                string.Format(
                    "{0}_{1}", this.EntityTableName(member.LocalMember.DeclaringType), member.LocalMember.Name));
            propertyCustomizer.Key(
                k => { k.Column(c => { c.Name(string.Format("Owner_{0}", member.LocalMember.DeclaringType.Name)); }); });
            propertyCustomizer.Index(
                idx =>
                {
                    idx.Base(1);
                    idx.Column("Idx");
                });
        }

        /// <summary>
        /// The map many to many.
        /// </summary>
        /// <param name="modelInspector">
        /// The model inspector.
        /// </param>
        /// <param name="member">
        /// The member.
        /// </param>
        /// <param name="collectionRelationManyToManyCustomizer">
        /// The collection relation many to many customizer.
        /// </param>
        private void MapManyToMany(
            IModelInspector modelInspector,
            PropertyPath member,
            IManyToManyMapper collectionRelationManyToManyCustomizer)
        {
            Type containingType = (member.LocalMember as PropertyInfo).PropertyType;
            collectionRelationManyToManyCustomizer.Column(
                string.Format("Target_{0}", containingType.GetGenericArguments().Last().Name));
        }

        /// <summary>
        /// The map many to one.
        /// </summary>
        /// <param name="modelInspector">
        /// The model inspector.
        /// </param>
        /// <param name="member">
        /// The member.
        /// </param>
        /// <param name="propertyCustomizer">
        /// The property customizer.
        /// </param>
        private void MapManyToOne(
            IModelInspector modelInspector, PropertyPath member, IManyToOneMapper propertyCustomizer)
        {
            propertyCustomizer.Column(this.QuoteKeyword(member.LocalMember.Name));
        }

        /// <summary>
        /// The map map.
        /// </summary>
        /// <param name="modelInspector">
        /// The model inspector.
        /// </param>
        /// <param name="member">
        /// The member.
        /// </param>
        /// <param name="propertyCustomizer">
        /// The property customizer.
        /// </param>
        private void MapMap(
            IModelInspector modelInspector, PropertyPath member, IMapPropertiesMapper propertyCustomizer)
        {
            propertyCustomizer.Table(
                string.Format(
                    "{0}_{1}", this.EntityTableName(member.LocalMember.DeclaringType), member.LocalMember.Name));
            propertyCustomizer.Key(
                k => { k.Column(c => { c.Name(string.Format("Owner_{0}", member.LocalMember.DeclaringType.Name)); }); });
        }

        /// <summary>
        /// The map map key.
        /// </summary>
        /// <param name="modelInspector">
        /// The model inspector.
        /// </param>
        /// <param name="member">
        /// The member.
        /// </param>
        /// <param name="mapKeyElementCustomizer">
        /// The map key element customizer.
        /// </param>
        private void MapMapKey(
            IModelInspector modelInspector, PropertyPath member, IMapKeyMapper mapKeyElementCustomizer)
        {
            mapKeyElementCustomizer.Column(
                c =>
                {
                    c.Name(
                        string.Format(
                            "Key_{0}",
                            (member.LocalMember as PropertyInfo).PropertyType.GetGenericArguments().First().Name));
                });
        }

        /// <summary>
        /// The map map key many to many.
        /// </summary>
        /// <param name="modelInspector">
        /// The model inspector.
        /// </param>
        /// <param name="member">
        /// The member.
        /// </param>
        /// <param name="mapKeyManyToManyCustomizer">
        /// The map key many to many customizer.
        /// </param>
        private void MapMapKeyManyToMany(
            IModelInspector modelInspector, PropertyPath member, IMapKeyManyToManyMapper mapKeyManyToManyCustomizer)
        {
        }

        /// <summary>
        /// The map one to many.
        /// </summary>
        /// <param name="modelInspector">
        /// The model inspector.
        /// </param>
        /// <param name="member">
        /// The member.
        /// </param>
        /// <param name="collectionRelationOneToManyCustomizer">
        /// The collection relation one to many customizer.
        /// </param>
        private void MapOneToMany(
            IModelInspector modelInspector, PropertyPath member, IOneToManyMapper collectionRelationOneToManyCustomizer)
        {
        }

        /// <summary>
        /// The map one to one.
        /// </summary>
        /// <param name="modelInspector">
        /// The model inspector.
        /// </param>
        /// <param name="member">
        /// The member.
        /// </param>
        /// <param name="propertyCustomizer">
        /// The property customizer.
        /// </param>
        private void MapOneToOne(
            IModelInspector modelInspector, PropertyPath member, IOneToOneMapper propertyCustomizer)
        {
        }

        /// <summary>
        /// The map property.
        /// </summary>
        /// <param name="modelInspector">
        /// The model inspector.
        /// </param>
        /// <param name="member">
        /// The member.
        /// </param>
        /// <param name="propertyCustomizer">
        /// The property customizer.
        /// </param>
        private void MapProperty(
            IModelInspector modelInspector, PropertyPath member, IPropertyMapper propertyCustomizer)
        {
            propertyCustomizer.Column(this.QuoteKeyword(member.LocalMember.Name));
        }

        /// <summary>
        /// The map set.
        /// </summary>
        /// <param name="modelInspector">
        /// The model inspector.
        /// </param>
        /// <param name="member">
        /// The member.
        /// </param>
        /// <param name="propertyCustomizer">
        /// The property customizer.
        /// </param>
        private void MapSet(
            IModelInspector modelInspector, PropertyPath member, ISetPropertiesMapper propertyCustomizer)
        {
            propertyCustomizer.Table(
                string.Format(
                    "{0}_{1}", this.EntityTableName(member.LocalMember.DeclaringType), member.LocalMember.Name));
            propertyCustomizer.Key(
                k => { k.Column(c => { c.Name(string.Format("Owner_{0}", member.LocalMember.DeclaringType.Name)); }); });
        }

        /// <summary>
        /// The map subclass.
        /// </summary>
        /// <param name="modelInspector">
        /// The model inspector.
        /// </param>
        /// <param name="type">
        /// The type.
        /// </param>
        /// <param name="subclassCustomizer">
        /// The subclass customizer.
        /// </param>
        private void MapSubclass(
            IModelInspector modelInspector, Type type, ISubclassAttributesMapper subclassCustomizer)
        {
        }

        /// <summary>
        /// The map union subclass.
        /// </summary>
        /// <param name="modelInspector">
        /// The model inspector.
        /// </param>
        /// <param name="type">
        /// The type.
        /// </param>
        /// <param name="unionSubclassCustomizer">
        /// The union subclass customizer.
        /// </param>
        private void MapUnionSubclass(
            IModelInspector modelInspector, Type type, IUnionSubclassAttributesMapper unionSubclassCustomizer)
        {
        }

        /// <summary>
        /// The quote keyword.
        /// </summary>
        /// <param name="word">
        /// The word.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        private string QuoteKeyword(string word)
        {
            switch (word.ToLower())
            {
                case "profile":
                    return "User_";
                default:
                    return word;
            }
        }

        #endregion
    }
}